#pragma once
#include<iostream>
#include<memory>
#include<string>
#include<functional>
#include<unordered_map>
#include<cerrno>
#include"Epoll.hpp"
#include"log.hpp"
#include"Socket.hpp"
#include"nocopy.hpp"
#include"SetNoBlock.hpp"
class Connection;
class TcpServer;
using func_t=std::function<void(std::weak_ptr<Connection>)>;//回调函数的类型
using except_func=std::function<void(std::weak_ptr<Connection>)>;
uint32_t EVENT_IN=(EPOLLIN|EPOLLET);//封装关心读事件且为ET模式
uint32_t EVENT_OUT=(EPOLLOUT|EPOLLET);//封装关心写事件且为ET模式
static const int defaultport=8821;
static const int buffer_size=128;
//Reactor的实现

//Connection 的作用：每一个fd对应一个connection对象，从fd上读写的数据保存在connection对象内创建好的缓冲区内，并实现对缓冲区读写的回调函数
//后期对Connection对象的管理等同于对每一个fd的管理

class Connection//先描述
{
    public:
    Connection(int sock)
    :_fd(sock)
    {

    }
    void SetHandler(func_t rcb,func_t scb,except_func ecb)
    {
        //设置三个回调函数
        _recv_cb=rcb;
        _send_cb=scb;
        _except_cb=ecb;
    }
    void SetWeakPtr(std::weak_ptr<TcpServer> tcpserverptr)
    {
        tcpserver_ptr=tcpserverptr;
    }
    void AppendInbuffer(const std::string &str)
    {
        in_buffer+=str;
    }
    void AppendOutbuffer(const std::string&str)
    {
        out_buffer+=str;
    }
     std::string& Inbuffer()
    {
        return in_buffer;
    }
    std::string& Outbuffer()
    {
        return out_buffer;
    }
    int GetFd()
    {
        return _fd;
    }
        ~Connection()
    {

    }
    private:
    int _fd;//文件描述符
    std::string in_buffer; //读取上来的数据（接收缓冲区）
    std::string out_buffer; //想要发送的数据 （发送缓冲区）

    public:
    func_t _recv_cb;//读取回调
    func_t _send_cb;//发送回调
    except_func _except_cb;//异常回调
    std::weak_ptr<TcpServer> tcpserver_ptr;
    std::string peerip_;
    uint16_t peerport_;
};

//std：：enable_shared_from_this<TcpServer>->必须公有继承，解决了不能从this对象构造智能指针的问题
class TcpServer:public std::enable_shared_from_this<TcpServer>,public nocopy
{
    static const int num=64;

    public:
    TcpServer(func_t onmessage,const uint16_t& port=defaultport)
    :port_(port),_quit(true),epoll_ptr(new Epoll()),listen_ptr(new Sock()),_Onmessage(onmessage)
    {

    }
    void AddConnection(int sock,uint32_t event,func_t rcb,func_t scb,except_func ecb,
    const std::string& ip="0.0.0.0",const uint16_t& port=0)
    {
        //1.给listenfd建立一个connection对象，将listenfd添加进connection中（包括其回调函数也要设置）
        //std::shared_ptr<Connection> new_connection(new Connection(sock));
        std::shared_ptr<Connection> new_connection=std::make_shared<Connection>(sock);

        new_connection->SetWeakPtr(shared_from_this());//返回当前对象的shared_ptr
       new_connection->SetHandler(rcb,scb,ecb);
       new_connection->peerip_=ip;
       new_connection->peerport_=port;
        //2.同时listenfd和connection放入_connections
        _connections.insert(std::make_pair(sock,new_connection));
        //3.sock同时设进内核
        epoll_ptr->EpollUpdate(EPOLL_CTL_ADD,sock,event);

        lg(DEBUG,"Add a cnonection success!!");

    }
    bool Init()
    {
        //服务器初始化
        epoll_ptr->EpollCreate();
        listen_ptr->Socket();
        //不仅要设置ET模式还需要将listenfd设为非阻塞
        SetNoBlock(listen_ptr->Fd());
        listen_ptr->Bind(port_);
        listen_ptr->Listen();
        lg(INFO,"Server Init success!! listenfd is:%d",listen_ptr->Fd());
        //将listenfd设入内核，创建connection对象，管理connection对象
        AddConnection(listen_ptr->Fd(),EVENT_IN,std::bind(&TcpServer::Accepter,this,std::placeholders::_1),nullptr,nullptr);
        return true;
    }
    
    //连接管理器
    void Accepter(std::weak_ptr<Connection> connect)
    {
        if(connect.expired())return;
        std::shared_ptr<Connection> connect_ptr=connect.lock();
        //因为ET模式倒逼程序员必须一次把本轮数据全部带走，因为只会提醒一次（从无到有，从有到多）
        while(true)
        {
            std::string peerip;
            uint16_t peerport;
          int sockfd=  listen_ptr->Accept(&peerport,&peerip);//这里是非阻塞读
          if(sockfd>0)
          {
            lg(DEBUG,"Get a new link ,sockfd is: %d, ip:port is ->  [%s:%d]",sockfd,peerip.c_str(),peerport);
            SetNoBlock(sockfd);//ET模式需要设置文件描述符为非阻塞

            //accept上来的sockfd需要进行读写异常等事件的处理
            AddConnection(sockfd,EVENT_IN,std::bind(&TcpServer::Recver,this,std::placeholders::_1),
            std::bind(&TcpServer::Sender,this,std::placeholders::_1),
            std::bind(&TcpServer::Excepter,this,std::placeholders::_1),
            peerip,peerport);
          }
          else if(errno==EWOULDBLOCK)break;//因为读取非阻塞文件读取不到数据会设置错误码EWOULDBLOCK
          else if(errno==EINTR)continue;//错误码为EINTR表示被中断，继续读取
          else
          {
            //走到这里才是真正的读取出错
            lg(ERROR,"Accept a link error!! errmessage is: %s",strerror(errno));
           // connect_ptr->_except_cb(connect_ptr);//出错调用它的异常回调函数？
            break;
          }
        }
    }
    void EnableEvent(int fd,bool readable,bool writeable)
    {
        uint32_t events=0;
        events|=(readable?EPOLLIN:0)|(writeable?EPOLLOUT:0)|EPOLLET;
        epoll_ptr->EpollUpdate(EPOLL_CTL_MOD,fd,events);
    }

    //事件管理器
    void Recver(std::weak_ptr<Connection> con)
    {
        if(con.expired())return;
        std::shared_ptr<Connection> connect_ptr =con.lock();
        //读事件处理,因为ET模式，所以要求一次把数据都读完
        std::cout<< "Recv a requst !!"<<std::endl;

        while(true)
        {
            char info_buffer[buffer_size];
            memset(info_buffer,0,sizeof(info_buffer));
            int sockfd=connect_ptr->GetFd();
           ssize_t n= recv(sockfd,info_buffer,sizeof(info_buffer),0);//这里不会阻塞，因为已经把fd设为非阻塞了
            if(n>0)
            {
                connect_ptr->AppendInbuffer(info_buffer);
                //std::cout<<"add inbuffer success:"<<connect_ptr->Inbuffer()<<std::endl;
            }
            else if(n==0)
            {
                lg(INFO,"Client quit, sockfd:%d, client info %s:%d",sockfd,connect_ptr->peerip_.c_str(),connect_ptr->peerport_);
                connect_ptr->_except_cb(connect_ptr);
                return;

            }
            else
            {
                 if(errno==EWOULDBLOCK)break;
            else if(errno==EINTR)continue;
            else
            {
                connect_ptr->_except_cb(connect_ptr);
                return;
            }
            }
           
        }
        _Onmessage(connect_ptr);//交给上层，把处理结果写入发送缓冲区

    }
    void Sender(std::weak_ptr<Connection> connection)
    {
        if(connection.expired())return;
        std::shared_ptr<Connection> connect_ptr =connection.lock();

        //写事件处理
        //1.因为epoll/select/poll对写事件的就绪是判断发送缓冲区是否有空间，有就就绪
        //2.因为发送缓冲区经常是有内容的也就是经常就绪
        //3.所以如果单纯设置EPOLOUT几乎每次都会就绪，几乎每次都会返回，浪费cpu资源
        //4.所以我们要按需设置，直接发，如果发送结束后发送缓冲区还有内容就关心，否则不关心
        int fd=connect_ptr->GetFd();
            std::string& outbuffer=connect_ptr->Outbuffer();

        while(true)//因为ET模式要求一次要把数据都读处理完，所以要不停发直到发送缓冲区没内容
        {
            ssize_t n=send(fd,outbuffer.c_str(),outbuffer.size(),0);
            if(n>0)
            {
                //发送成功后删除发送缓冲区里的内容（发了多少删多少）
                outbuffer.erase(0,n);
                if(outbuffer.empty())
                break;//如果发送缓冲区已经发完了就退出
            }
            else if(n==0)
            {
                //接收端关闭
                lg(INFO,"Client closed!!");
                return;
            }
            else
            {
                if(errno==EWOULDBLOCK)break;//不是真正出错
                else if(errno=EINTR)continue;//不是真正出错
                else
                {
                    //真正出错
                    lg(WARNING,"sockfd : %d ,client info %s:%d, send error...",fd,connect_ptr->peerip_.c_str(),connect_ptr->peerport_);
                    connect_ptr->_except_cb(connect_ptr);
                    return;
                }
            }
           
        }
        //再判断发送缓冲区是否为空，如果有内容再关心，没内容就设为不关心
         if(!outbuffer.empty())
            {
                EnableEvent(connect_ptr->GetFd(),true,true);
            }
            else
            {
                EnableEvent(connect_ptr->GetFd(),true,false);
            }
    }
    void Excepter(std::weak_ptr<Connection> connect)
    {
        if(connect.expired())return;
        std::shared_ptr<Connection> connect_ptr =connect.lock();

        int fd=connect_ptr->GetFd();
        //异常事件处理
        //1.在epoll中移除事件
        epoll_ptr->EpollUpdate(EPOLL_CTL_DEL,fd,0);
        //2.关闭异常文件描述符
        close(fd);
        //3.删除connections里管理的连接
        _connections.erase(fd);

    }
    

    bool IsConnectionSafe(int fd)
    {
        //对fd是否存在_connections内进行判断 //安全检测//确保存在
        auto iterator=_connections.find(fd);
        if(iterator==_connections.end())return false;
        return true;
    }
    void Dispatcher(int timeout)
    {
           int n= epoll_ptr->EpollWait(events,num,timeout);//这个events获取到的struct epoll_event结构体如果之前只设置了event并没有把fd也设进去的话，在往后不会获取得到fd
           //返回值n为已就绪事件的fd个数且是连续事件
           for(int i=0;i<n;i++)
           {
            //进到这里来说明一定有事件就绪
            std::cout<<"get a new event!!"<<std::endl;
            int sockfd=events[i].data.fd;
            uint32_t event=events[i].events;
            //判断event是什么事件
            // if(event&EPOLLERR)event|=(EPOLLIN|EPOLLOUT);//把异常错误事件都转换为读写问题？
            // if(event&EPOLLHUP)event|=(EPOLLIN|EPOLLOUT);
            if((event&EPOLLIN)&&IsConnectionSafe(sockfd))
            {
                if(_connections[sockfd]->_recv_cb)
                _connections[sockfd]->_recv_cb(_connections[sockfd]);
            }
            if((event&EPOLLOUT)&&IsConnectionSafe(sockfd))
            {
                if(_connections[sockfd]->_send_cb)
                _connections[sockfd]->_send_cb(_connections[sockfd]);
            }
           }
    }
    void Pool()
    {
        //服务器运行起来
        _quit=false;

        while(!_quit)
        {       
            Dispatcher(3000);//事件派发器
            PrintConnection();//debug
        }
        _quit=true;
    }
    void PrintConnection()
    {
        std::cout<<"Connection list : ";
        for(auto& connection:_connections)
        {
            std::cout<<connection.second->GetFd()<<" ";
            std::cout<<"inbuffer: "<<connection.second->Inbuffer();
            
        }
        std::cout<<std::endl;
    }
    ~TcpServer()
    {

    }
    private:
    uint16_t port_;//服务器端口
    bool _quit;
    std::shared_ptr<Epoll> epoll_ptr;//epoll模型
    std::shared_ptr<Sock> listen_ptr;//套接字
    std::unordered_map<int,std::shared_ptr<Connection>> _connections;//对所有fd对应的connection的组织（再组织）
    struct epoll_event events[num];//从内核中捞数据上来（捞上来已就绪事件）

    func_t _Onmessage;

};